<?php
/**
 * Created by PhpStorm.
 * Author: 芸签cms www.yunqiancms.com
 * User: sun
 * Date: 2022/7/13
 * Time: 10:45 AM
 */
namespace app\backend\modules\theme\services;

use app\common\exceptions\BizException;
use app\common\helpers\Cache;
use app\common\helpers\Utils;
use app\common\models\Theme;
use app\common\models\ThemeFile;
use app\common\models\ThemeFileConfigLog;
use app\common\services\AutoUpdate;
use app\common\services\theme\ConfigDataFactory;
use app\common\services\Storage;
use ZipArchive;

class ThemeService
{
    public function getList(){
        $query = Theme::site()->select();
        $list = $query
            ->orderBy('id', 'desc')
            ->paginate(10);
        $list->map(function ($v){
            $v->type_desc = Theme::$typeDescMap[$v['type']];
        });
        return $list;
    }
    public function getAll(){
        $query = Theme::site()->select();
        $list = $query
            ->orderBy('id', 'desc');
        return $list;
    }

    /**
     * 使用本地模版
     * @param $theme
     * @param $name
     * @throws BizException
     */
    public function usedLocal($params){
        $params['remote_id'] = 0;
        $this->usedTheme($params);
    }
    /**
     * 未安装模版列表
     * @return array
     */
    public function getUnInstalledList(){
        $themePath = config('app.theme_path');
        $themesDirs = scandir($themePath);

        $themeModel = Theme::site()->select(['theme'])
            ->get();
        $installedTheme = $themeModel->pluck('theme')->toArray();

        $themesDirs = array_diff($themesDirs, $installedTheme);

        $themes = [];
        foreach ($themesDirs as $dir) {
            $manifest = $themePath."/$dir/manifest.json";
            if (is_file($manifest)) {
                $manifest       = file_get_contents($manifest);
                $theme          = json_decode($manifest, true);
                unset($theme['files']);
                $theme['theme'] = $dir;
                $theme['type_desc'] = Theme::$typeDescMap[$theme['type']];
                array_push($themes, $theme);
            }
        }
        return $themes;
    }

    /**
     * 获取本地模版列表(模版市场已下载的和用户自己开发的)
     * @return array
     */
    public function getLocalList(){
        $themePath = config('app.theme_path');
        $themesDirs = scandir($themePath);
        $themes = [];
        foreach ($themesDirs as $dir) {
            $manifest = $themePath."/$dir/manifest.json";
            if (is_file($manifest)) {
                $manifest       = file_get_contents($manifest);
                $theme          = json_decode($manifest, true);
                unset($theme['files']);
                $theme['theme'] = $dir;
                $theme['type_desc'] = Theme::$typeDescMap[$theme['type']];
                array_push($themes, $theme);
            }
        }
        return $themes;
    }
    public function usedMarketTheme($params){
        $domain = request()->getHttpHost();
        $res =  (new AutoUpdate())->themeInstall($params['id'], $domain);
        //\Log::debug('------插件安装升级------',json_encode($res,true));
        if ($res) {
            $this->downloadTheme($params);
        } else {
            throw new BizException('安装失败,插件不存在');
        }
        $this->usedTheme($params);
    }
    /**
     * 使用模版
     * @param $params
     * @throws BizException
     */
    public function usedTheme($params){

        $themePath = config('app.theme_path');
        $theme = $params['theme'];
        $remoteId = $params['remote_id'];
        $name = $params['name'];
        $manifest = $themePath."/$theme/manifest.json";
        if (is_file($manifest)) {
            $manifest = file_get_contents($manifest);
            $themeData = json_decode($manifest, true);
            $themeData['theme'] = $theme;
            $themeModel = new Theme();
            $themeModel->site_id = \YunCms::siteId();
            $themeModel->name = $name;
            $themeModel->remote_id = $remoteId;
            $themeModel->type = $themeData['type'];
            $themeModel->theme = $themeData['theme'];
            $themeModel->thumb = $themeData['thumb'];
            $themeModel->version = $themeData['version'];
            $themeModel->is_default = intval($params['is_default']);
            $themeModel->status = Theme::STATUS_NORMAL;//设置默认开启
            $themeModel->description = $themeData['description'];
            if($themeModel->save()){
                $themeId = $themeModel['id'];
                $files = $themeData['files'];
                $now = time();
                $insertDataArr = [];
                foreach ($files as $v){
                    $configFile = $themePath.'/'.$theme.'/'.$v['config'];
                    if(is_file($configFile)){
                        $config = file_get_contents($configFile);
                        $configData = json_decode($config, true);
                        $insertData = [];
                        $insertData['site_id'] = \YunCms::siteId();
                        $insertData['theme_id'] = $themeId;
                        $insertData['name'] = $configData['name'];
                        $insertData['file_name'] = $configData['fileName'];
                        $insertData['layout'] = strval($configData['layout']);
                        $insertData['type'] = $configData['type'];
                        $insertData['file'] = $v['file'];
                        $insertData['description'] = $configData['description'];
                        $insertData['page_info'] = json_encode([],true);
                        $insertData['configs'] = json_encode($configData['blocks'],true);
                        $insertData['created_at'] = $now;
                        array_push($insertDataArr,$insertData);
                    }else{
                        throw new BizException($v['config']."配置文件不存在");
                    }
                }
                if(count($insertDataArr)>0){
                    ThemeFile::insert($insertDataArr);
                }else{
                    \Log::debug(__CLASS__.'installTheme $insertDataArr为空');
                }
            }else{
                throw new BizException("模版安装失败，请稍后重试");
            }
        } else {
            \Log::error('主配置文件不存在,pate:',$manifest);
            throw new BizException("主配置文件不存在");
        }
    }
    public function downloadTheme($theme)
    {
        $name = $theme['theme'];
        $domain = request()->getHttpHost();
        if (!$name){
            throw new BizException('名字不存在！');
        }
        //Prepare download
        $tmp_dir = storage_path('theme-download-temp');
        $tmp_path = $tmp_dir.'/tmp_'.$name.'.zip';
        if (!is_dir($tmp_dir)) {
            if (false === mkdir($tmp_dir)) {
                throw new BizException('临时文件夹创建失败');
            }
        }
        $ctx = stream_context_create([
            "ssl"=>[
                "verify_peer"=>false,
                "verify_peer_name"=>false,
            ]
        ]);
        $themePath = config('app.theme_path');

        $autoUpdate = new AutoUpdate();

        $themeVersionRes = $autoUpdate->getThemeVersionByName($domain,$name)['data'];
        \Log::debug('$themeVersionRes',$themeVersionRes);
        $url = $themeVersionRes['url'];
        $url = $url . "?token=" . $autoUpdate->getToken() . '&domain=' . $domain;
        //Connection check
        if (!$fp = @fopen($url, 'rb', false, $ctx)) {
            throw new BizException('url 打不开！');
        }
        try {
            Utils::download($url, $tmp_path);

        } catch (\Exception $e) {
            Storage::removeDir($tmp_dir);
            throw new BizException('下载失败！');
        }


        $zip = new ZipArchive();
        $res = $zip->open($tmp_path);
        \Log::debug('$zip$res',$res);
        if ($res === true) {
            try {
                $zip->extractTo($themePath);
                \Log::debug($name.'解压成功 path:',$themePath);
            } catch (\Exception $e) {
                $zip->close();
                \Log::debug('解压失败');
                \Log::debug($e);
                //Storage::removeDir($tmp_dir);
                throw new BizException('解压失败！');
            }
        } else {
            $zip->close();
            //Storage::removeDir($tmp_dir);
            throw new BizException('解压失败！');
        }
        $zip->close();

        //Clean temporary working dir
        Storage::removeDir($tmp_dir);
        Cache::flush();
        //event(new PluginWasInstalled($plugin));
    }
    public function updateMarketTheme($id){

    }
    /**
     * 文件只能添加，不能删除；配置只能添加，不能删除和修改键名或值类型，否则客户更新会有问题！！！
     * 至更新本地配置，模版文件更新放到系统一起
     * @param $id
     * @throws BizException
     */
    public function update($id){
        $themePath = config('app.theme_path');
        $themeModel = Theme::site()->select()
            ->where('id',$id)
            ->first();
        $theme = $themeModel['theme'];
        $name = $themeModel['name'];
        $manifest = $themePath."/$theme/manifest.json";
        if (is_file($manifest)) {
            $manifest = file_get_contents($manifest);
            $themeData = json_decode($manifest, true);
            $themeData['theme'] = $theme;
            $themeModel->thumb = $themeData['thumb'];
            $themeModel->version = $themeData['version'];
            $themeModel->description = $themeData['description'];
            if($themeModel->save()){
                $themeId = $themeModel['id'];
                $files = $themeData['files'];
                $oldFilesData = ThemeFile::site()->select()
                    ->where('theme_id',$themeId)
                    ->get();
                $oldFiles = $oldFilesData->pluck('file')->toArray();
                $newAddFiles = [];
                $filesMap = [];
                foreach ($files as $v){
                    if(!in_array($v['file'],$oldFiles)){
                        array_push($newAddFiles,$v);
                    }
                    $filesMap[$v['file']] = $v;
                }
                $now = time();
                $insertDataArr = [];
                foreach ($newAddFiles as $v){
                    $configFile = $themePath.'/'.$theme.'/'.$v['config'];
                    if(is_file($configFile)){
                        $config = file_get_contents($configFile);
                        $configData = json_decode($config, true);
                        $insertData = [];
                        $insertData['site_id'] = \YunCms::siteId();
                        $insertData['theme_id'] = $themeId;
                        $insertData['name'] = $configData['name'];
                        $insertData['file_name'] = $configData['fileName'];
                        $insertData['layout'] = strval($configData['layout']);
                        $insertData['type'] = $configData['type'];
                        $insertData['file'] = $v['file'];
                        $insertData['description'] = $configData['description'];
                        $insertData['page_info'] = json_encode([],true);
                        $insertData['configs'] = json_encode($configData['blocks'],true);
                        $insertData['created_at'] = $now;
                        array_push($insertDataArr,$insertData);
                    }else{
                        throw new BizException($v['config']."配置文件不存在");
                    }
                }
                foreach ($oldFilesData as $v){
                    $configFile = $themePath.'/'.$theme.'/'.$filesMap[$v['file']]['config'];
                    \Log::debug('$configFile',$configFile);
                    if(is_file($configFile)){
                        $config = file_get_contents($configFile);
                        $configData = json_decode($config, true);
                        $noUpdateConfigs = [];
                        $oldConfigs = $v['configs']?json_decode($v['configs'],true):[];
                        \Log::debug('$oldConfigs',$oldConfigs);
                        foreach ($oldConfigs as $key=>$val){
                            //这里的值不修改
                            $noUpdateConfigs[$key]['order'] = $val['order'];
                            $noUpdateConfigs[$key]['isShow'] = $val['isShow'];
                            foreach ($val['configs'] as $configKey=>$value){
                                $noUpdateConfigs[$key]['configs'][$configKey]['value'] = $value['value'];
                            }
                        }
                        //新的配置中不可以修改的键值用旧的替换掉
                        foreach ($configData['blocks'] as $key=>$val){
                            if($noUpdateConfigs[$key]['order']){
                                //已经设置了排序，按之前的排序
                                $configData['blocks'][$key]['order'] = $noUpdateConfigs[$key]['order'];
                            }
                            if($noUpdateConfigs[$key]['isShow']===false){
                                //如果是隐藏不显示，不要用新的值替换，显示会出问题
                                $configData['blocks'][$key]['isShow'] = boolval($noUpdateConfigs[$key]['isShow']);
                            }
                            foreach ($val['configs'] as $configKey=>$value){
                                $configData['blocks'][$key]['configs'][$configKey]['value'] = $noUpdateConfigs[$key]['configs'][$configKey]['value'];
                            }
                        }
                        \Log::debug('$configData',$configData);
                        ThemeFile::where('id',$v['id'])
                            ->update([
                                    'name'=>$configData['name'],
                                    'description'=>$configData['description'],
                                    'file_name' => $configData['fileName'],
                                    'layout' => strval($configData['layout']),
                                    'configs'=> json_encode($configData['blocks'],true)
                                ]);
                    }else{
                        throw new BizException($v['config']."配置文件不存在");
                    }
                }
                if(count($insertDataArr)>0){
                    ThemeFile::insert($insertDataArr);
                }

            }else{
                throw new BizException("模版更新失败，请稍后重试");
            }
        } else {
            throw new BizException("主配置文件不存在");
        }

    }
    public function changeStatus($id){
        $model = Theme::site()->select()
            ->where('id',$id)
            ->first();
        if (empty($model)) {
            throw new BizException('未找到数据');
        }
        $status=Theme::STATUS_NORMAL;
        if($model->status==Theme::STATUS_NORMAL){
            $status= Theme::STATUS_BAN;
        }
        $model->status = $status;
        $model->save();
    }

    /**
     * 设置为默认模版
     * @param $id
     * @throws BizException
     */
    public function setDefault($id){
        $model = Theme::site()->select()
            ->where('id',$id)
            ->first();
        if (empty($model)) {
            throw new BizException('未找到数据');
        }
        $type = $model['type'];
        //同一类型的模版只有一个可以是默认的
        $isDefault=Theme::STATUS_NORMAL;
        if($type==Theme::TYPE_ALL){
            //all 其他PC、H5和all改成非默认
            Theme::site()->update(['is_default' => Theme::STATUS_BAN]);
        }else{
            //PC 其他pc和all改成非默认
            //H5 其他H5和all改成非默认
            Theme::site()
                ->whereIn('type',[$type,Theme::TYPE_ALL])
                ->update(['is_default' => Theme::STATUS_BAN]);
        }
        $model->is_default = $isDefault;
        $model->save();
    }

    /**
     * 根据模版id获取页面信息列表
     * @param $themeId
     * @param int $noLayout 不包括布局页
     * @return \app\common\models\Collection|array
     */
    public function getPageList($themeId,$noLayout=1){
        $list = [];
        if($themeId){
            $list = ThemeFile::site()->select(['id','site_id','theme_id','name','type','file','description'])
                ->where('theme_id',$themeId);
            if($noLayout){
                $list = $list->where('type','!=',ThemeFile::TYPE_LAYOUT);
            }
            $list = $list->get();
        }
        return $list;
    }

    /**
     * 获取模版页面详情
     * @param $pageId
     * @return ThemeFile
     */
    public function getPageDetail($pageId){
        $res = ThemeFile::site()->select()
            ->where('id',$pageId)
            ->first();
        $configs = $res['configs']?json_decode($res['configs'],true):[];
        $res['page_info'] = $res['page_info']?json_decode($res['page_info'],true):[];
        $pageConfigs = [];
        if($res['layout']){
            //存在布局文件，读取布局文件的配置信息
            $layoutInfo = ThemeFile::site()->select()
                ->where('theme_id',$res['theme_id'])
                ->where('file_name',$res['layout'])
                ->first();
            if($layoutInfo['id']){
                //合并配置中block信息
                $layoutConfigs = $layoutInfo['configs']?json_decode($layoutInfo['configs'],true):[];
                foreach ($layoutConfigs as $k=>$v){
                    if($k!='slot'){
                        //todo 是否考虑多个slot
                        $pageConfigs[$k] = $v;
                    }else{
                        //slot后面插入页面的blocks
                        foreach ($configs as $key=>$val){
                            $pageConfigs[$key] = $val;
                        }
                    }
                }
                unset($pageConfigs['slot']);
            }
        }else{
            $pageConfigs = $configs;
        }
        foreach ($pageConfigs as $k=>$v){
            foreach ($pageConfigs[$k]['configs'] as $key=>$val){
                if($val['dataType']){
                    $pageConfigs[$k]['configs'][$key]['datas'] = ConfigDataFactory::create($val['dataType'])->getData($val['value'],true);
                }else{
                    $pageConfigs[$k]['configs'][$key]['datas'] = $val['value'];
                }
            }
        }
        $res['configs'] = $pageConfigs;
        return $res;
    }

    /**
     * 预览前保存临时数据
     * @param $params page_id/configs/page_info
     * @throws \Exception
     */
    public function savePreview($params){
        $pageId = $params['page_id'];
        $configs = $params['configs'];
        $pageInfo = $params['page_info'];
        $fileModel = ThemeFile::site()->select()
            ->where('id',$pageId)
            ->first();
        if(!$fileModel['id']){
            throw new BizException('未找到数据');
        }
        if($fileModel['layout']){
            //存在布局文件，读取布局文件的配置信息
            $layoutInfo = ThemeFile::site()->select()
                ->where('theme_id',$fileModel['theme_id'])
                ->where('file_name',$fileModel['layout'])
                ->first();
            if($layoutInfo['id']){
                //提取配置中layout的block信息
                $layoutConfigs = $layoutInfo['configs']?json_decode($layoutInfo['configs'],true):[];
                foreach ($layoutConfigs as $k=>$v){
                    if($configs[$k]){
                        $layoutConfigs[$k] = $configs[$k];
                        unset($configs[$k]);//$configs去掉layout配置
                    }

                }
                $layoutPageInfo = $layoutInfo['page_info']?json_decode($layoutInfo['page_info'],true):[];
                //保存layout配置信息
                $this->saveConfigLog([
                    'theme_file_id'=>$layoutInfo['id'],
                    'configs'=>$layoutConfigs,
                    'page_info'=>$layoutPageInfo,
                    'type'=>ThemeFileConfigLog::TYPE_SAVE,
                ]);
            }
        }
        $this->saveConfigLog([
            'theme_file_id'=>$pageId,
            'configs'=>$configs,
            'page_info'=>$pageInfo,
            'type'=>ThemeFileConfigLog::TYPE_PREVIEW,
        ]);
    }

    /**
     * 保存配置数据(暂时不区分保存和发布，如果区分的话页面中的链接跳转到其他页面也要区分，比较麻烦)
     * @param $params
     * @throws \Exception
     */
    public function savePage($params){
        $pageId = $params['page_id'];
        $configs = $params['configs'];
        $pageInfo = $params['page_info'];
        $fileModel = ThemeFile::site()->select()
            ->where('id',$pageId)
            ->first();
        if(!$fileModel['id']){
            throw new BizException('未找到数据');
        }
        if($fileModel['layout']){
            //存在布局文件，读取布局文件的配置信息
            $layoutInfo = ThemeFile::site()->select()
                ->where('theme_id',$fileModel['theme_id'])
                ->where('file_name',$fileModel['layout'])
                ->first();
            if($layoutInfo['id']){
                //提取配置中layout的block信息
                $layoutConfigs = $layoutInfo['configs']?json_decode($layoutInfo['configs'],true):[];
                foreach ($layoutConfigs as $k=>$v){
                    if($configs[$k]){
                        $layoutConfigs[$k] = $configs[$k];
                        unset($configs[$k]);//$configs去掉layout配置
                    }

                }
                $layoutPageInfo = $layoutInfo['page_info']?json_decode($layoutInfo['page_info'],true):[];
                //保存layout配置信息
                $this->saveConfigLog([
                    'theme_file_id'=>$layoutInfo['id'],
                    'configs'=>$layoutConfigs,
                    'page_info'=>$layoutPageInfo,
                    'type'=>ThemeFileConfigLog::TYPE_SAVE,
                ]);
                $layoutInfo->configs = $layoutConfigs?json_encode($layoutConfigs,true):json_encode([],true);
                $layoutInfo->save();
            }
        }
        //保存页面配置信息
        $this->saveConfigLog([
            'theme_file_id'=>$pageId,
            'configs'=>$configs,
            'page_info'=>$pageInfo,
            'type'=>ThemeFileConfigLog::TYPE_SAVE,
        ]);
        $fileModel->page_info = $pageInfo?json_encode($pageInfo,true):json_encode([],true);
        $fileModel->configs = $configs?json_encode($configs,true):json_encode([],true);
        $fileModel->save();
    }

    /**
     * 保存页面编辑记录
     * @param $params theme_file_id/configs/page_info/type
     * @throws \Exception
     */
    public function saveConfigLog($params){
        $themeFileId = $params['theme_file_id'];
        $configs = $params['configs'];
        $pageInfo = $params['page_info'];
        $type = $params['type'];
        $model = new ThemeFileConfigLog();
        $model->site_id = \YunCms::siteId();
        $model->theme_file_id = $themeFileId;
        $model->type = $type;
        $model->page_info = $pageInfo?json_encode($pageInfo,true):json_encode([],true);
        $model->configs = $configs?json_encode($configs,true):json_encode([],true);
        $model->save();
        $maxStorageLog = ThemeFileConfigLog::site()->select('id')
            ->where('theme_file_id',$themeFileId)
            ->where('type',$type)
            ->orderBy('id','desc')
            ->skip(10)//暂时最多保留10条,后期可以放到设置里
            ->first();
        if($maxStorageLog && $maxStorageLog['id']){
            //todo 待测试
            ThemeFileConfigLog::site()
                ->where('id','<',$maxStorageLog['id'])
                ->delete();
        }

    }
}